/*************************************************************
*
* DS_SPU.CPP
* 
*************************************************************/

#include "ap\ap_comm.h"

#ifdef _AP_PS3
#include <cell/atomic.h>
#endif

#include "ap\ap_spu_realloc_ps3.h"
#include "ds_spu.h"

#ifdef SPU
//
// Static variables
//
static uint8_t atomicBuf[128] __attribute__((aligned(128)));
static void   *dsVECTOR_SPU_CONTROLLER_ReallocContext;

#define LOAD_LINE() mfc_getllar(atomicBuf, vec, 0, 0); mfc_read_atomic_status()
#define TRY_COMMIT() mfc_putllc(atomicBuf, vec, 0, 0); if_rare (mfc_read_atomic_status() != 0) continue

/*************************************************************
* dsVECTOR_SPU_LengthAdd ()
*
*************************************************************/
int dsVECTOR_SPU_LengthAdd(int* val, int count)
{
   return cellAtomicAdd32(reinterpret_cast<uint32_t*>(atomicBuf), reinterpret_cast<uintptr_t>(val), count);
}
/*************************************************************
* dsVECTOR_SPU_BeginWrite ()
*
*************************************************************/
static void* dsVECTOR_SPU_BeginWrite(void *spuReallocContext, uintptr_t vec, void*& data, int& length, int& capacity, int& rwLock, apCL& cl, int elementSize)
{
   int reallocReq = 0;
   
   while (true) {
      // lock & load
      LOAD_LINE();
      
      // if we're write-locked or reallocation is requested (not by us), wait
      STRONG_ASSERT(rwLock == -1 || rwLock >= 0);
      if (rwLock == -1 || (rwLock & 1) != reallocReq) {
         continue;
      }
      
      if_frequent (length <= capacity) {
         // if we have enough capacity, just grab a read lock
         rwLock += 2;
         TRY_COMMIT();
         return data;
      } else {
         // need to reallocate; can we do it right now?
         if (rwLock == reallocReq) {
            // yes! write-lock it
            rwLock = -1;
            TRY_COMMIT();
            
            // finally! do a reallocation and update data/capacity
            int newCapacity = max(length + 16, max(capacity + capacity / 2, 64));
            void* newData = apSpuRealloc(spuReallocContext, data, newCapacity * elementSize, cl);
            
            // commit it
            while (true) {
               // lock & load
               LOAD_LINE();
               
               STRONG_ASSERT(rwLock == -1);
               data = newData;
               capacity = newCapacity;
               rwLock = 2;
               TRY_COMMIT();
               
               return newData;
            }
         } else if (reallocReq == 0) {
            // we're the first one to realloc & there are some readers; request reallocation and wait
            rwLock |= 1;
            TRY_COMMIT();
            reallocReq = 1;
         }
      }
   }
}
/*************************************************************
* dsVECTOR_SPU_BeginWrite ()
*
*************************************************************/
void* dsVECTOR_SPU_BeginWrite (void *spuReallocContext, uintptr_t vec, int dataOffset, int lengthOffset, int capacityOffset, int rwLockOffset, int clOffset, int elementSize)
{
   // 128b aligned rebase
   dataOffset += vec & 127;
   lengthOffset += vec & 127;
   capacityOffset += vec & 127;
   rwLockOffset += vec & 127;
   clOffset = (clOffset < 0) ? clOffset : (clOffset + (vec & 127));
   vec &= ~127;
   
   // check that everything is in a single cache line
   STRONG_ASSERT(dataOffset < 128 && lengthOffset < 128 && capacityOffset < 128 && rwLockOffset < 128 && clOffset < 128);
   
   // get data refs
   apCL dummyCL(NULL, 0);
   
   void*& data = *reinterpret_cast<void**>(atomicBuf + dataOffset);
   int& length = *reinterpret_cast<int*>(atomicBuf + lengthOffset);
   int& capacity = *reinterpret_cast<int*>(atomicBuf + capacityOffset);
   int& rwLock = *reinterpret_cast<int*>(atomicBuf + rwLockOffset);
   apCL& cl = clOffset < 0 ? dummyCL : *reinterpret_cast<apCL*>(atomicBuf + clOffset);
   
   return dsVECTOR_SPU_BeginWrite(spuReallocContext, vec, data, length, capacity, rwLock, cl, elementSize);
}
/*************************************************************
* dsVECTOR_SPU_EndWrite ()
*
*************************************************************/
void dsVECTOR_SPU_EndWrite (uintptr_t vec, int rwLockOffset)
{
   // 128b aligned rebase
   rwLockOffset += vec & 127;
   vec &= ~127;
   
   // check that everything is in a single cache line
   STRONG_ASSERT(rwLockOffset < 128);
   
   while (true) {
      // lock & load
      LOAD_LINE();
      
      // get data
      int& rwLock = *reinterpret_cast<int*>(atomicBuf + rwLockOffset);
      
      // should not be write-locked, should have at least one reader
      STRONG_ASSERT(rwLock >= 2);
      rwLock -= 2;
      
      // store conditional
      TRY_COMMIT();
      return;
   }
}

#undef TRY_COMMIT
#undef LOAD_LINE

/*************************************************************
* dsVECTOR_SPU_CONTROLLER_SetSPUReallocContext ()
*
*************************************************************/
void  dsVECTOR_SPU_CONTROLLER_SetSPUReallocContext (void *_spuReallocContext) 
{ 
   dsVECTOR_SPU_CONTROLLER_ReallocContext = _spuReallocContext;
}
/*************************************************************
* dsVECTOR_SPU_CONTROLLER_GetSPUReallocContext ()
*
*************************************************************/
void* dsVECTOR_SPU_CONTROLLER_GetSPUReallocContext (void) 
{ 
   return dsVECTOR_SPU_CONTROLLER_ReallocContext;
}

#endif

//
// End-of-file DS_SPU.CPP
//
